package info.rflow;

import info.rflow.graph.RWorkflowScene;
import org.netbeans.api.visual.model.ObjectSceneEventType;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;


/**
 * @author Holger Brandl
 */
public class SessionManager {

    private RFlow rFlow;

    List<WorkflowContext> workflowContexts = new ArrayList<WorkflowContext>();
    private WorkflowContext activeWFContext;

    private List<SesManListener> sesManListeners = new ArrayList<SesManListener>();
    private WorkflowSceneListener sceneListener;

    public final static String FORMAT_SUFFIX = ".R";


    public SessionManager(final RFlow rFlow) {

        this.rFlow = rFlow;
        sceneListener = new WorkflowSceneListener(rFlow);
    }


    public void setActiveWFContext(WorkflowContext workflowContext) {
        if (this.activeWFContext != null) {
            RWorkflowScene rWorkflowScene = this.activeWFContext.getWorkflowScene();
            rWorkflowScene.removeObjectSceneListener(sceneListener);
        }

        this.activeWFContext = workflowContext;

        sceneListener.setContext(this.activeWFContext);

        this.activeWFContext.getWorkflowScene().addObjectSceneListener(sceneListener, ObjectSceneEventType.OBJECT_SELECTION_CHANGED);

        for (SesManListener listener : sesManListeners)
            listener.newActiveScene(workflowContext);
    }


    public void unRegisterSceneContext(WorkflowContext sc) {
        int tabIndex = workflowContexts.indexOf(sc);

        workflowContexts.remove(sc);
        if (workflowContexts.size() > 0) {
            setActiveWFContext(workflowContexts.get(tabIndex > 0 ? tabIndex - 1 : 0));
        } else {
            setActiveWFContext(new WorkflowContext(null));
        }

        for (SesManListener listener : sesManListeners) {
            listener.removedScene(sc);
        }

        rFlow.invalidate();
    }


    public WorkflowContext getActiveWorkflow() {
        return activeWFContext;
    }


    public List<WorkflowContext> getWorkflows() {
        return workflowContexts;
    }


    public void addWorkflow(WorkflowContext sc) {
        workflowContexts.add(sc);

        for (SesManListener listener : sesManListeners) {
            listener.addedScene(sc);
        }
    }


    public void selectWorkflow2Load() {
        File rScriptFile = null;

        if (Utils.isMacOSX()) {
            FileDialog f = new FileDialog(rFlow, "Open R-Worfklow", FileDialog.LOAD);
            //      JFileChooser f = new JFileChooser(this, "Load Data", FileDialog.LOAD);
            f.setFile("");
            f.setDirectory(Utils.getPrefs().get("last.load.wf.directory", "."));
            f.setFilenameFilter(new FilenameFilter() {
                @Override
                public boolean accept(File file, String s) {
                    return s.endsWith(".R");
                }
            });
            f.setVisible(true);

            if (f.getFile() != null) {
                rScriptFile = new File(f.getDirectory() + File.separatorChar + f.getFile());
            }

        } else {     //other platforms

            JFileChooser jfc = new JFileChooser(Utils.getPrefs().get("last.load.wf.directory", "."));

            jfc.setMultiSelectionEnabled(false);
            jfc.setFileFilter(new FileFilter() {

                public boolean accept(File f) {
                    return (f.getName().endsWith(SessionManager.FORMAT_SUFFIX) && f.isFile()) || f.isDirectory();
                }


                public String getDescription() {
                    return "R-Script (*" + SessionManager.FORMAT_SUFFIX + ")";
                }
            });

            int status = jfc.showOpenDialog(rFlow);
            if (status == JFileChooser.APPROVE_OPTION) {
                rScriptFile = jfc.getSelectedFile();
                loadWorkflow(rScriptFile);
                rFlow.updateRecentFiles(rScriptFile);

                Utils.getPrefs().put("last.load.wf.directory", rScriptFile.getParent());
            }
        }

        if (rScriptFile != null) {
            loadWorkflow(rScriptFile);
            rFlow.updateRecentFiles(rScriptFile);

            Utils.getPrefs().put("last.load.wf.directory", rScriptFile.getParent());
        }
    }


    public void loadWorkflow(File rFile) {
        WorkflowContext newWorkflowContext = new WorkflowContext(rFile);
        addWorkflow(newWorkflowContext);
        setActiveWFContext(newWorkflowContext);
    }


    /**
     * Returns true is the scene was saved.
     */
    public boolean saveWorkflowAs(WorkflowContext workflowContext) {

        // select a file
        JFileChooser jfc = new JFileChooser(new File("."));
        jfc.setMultiSelectionEnabled(false);
        jfc.setFileFilter(new FileFilter() {

            public boolean accept(File f) {
                return (f.getName().endsWith(SessionManager.FORMAT_SUFFIX) && f.isFile()) || f.isDirectory();
            }


            public String getDescription() {
                return "R-Script (*" + SessionManager.FORMAT_SUFFIX + ")";
            }
        });

        int status = jfc.showSaveDialog(rFlow);
        if (status == JFileChooser.APPROVE_OPTION) {
            File rFile = jfc.getSelectedFile();
            if (!rFile.getName().endsWith(FORMAT_SUFFIX))
                rFile = new File(rFile.getAbsolutePath() + FORMAT_SUFFIX);

            workflowContext.setLocation(rFile);

            if (workflowContext.equals(rFlow.getSesMan().getActiveWorkflow()))
                rFlow.setSceneName(workflowContext, rFile.getName());

            saveWorkflow(workflowContext);
            return true;
        }

        return false;
    }


    public boolean saveWorkflow(WorkflowContext workflowContext) {
        File rFileLocation = workflowContext.getLocation();

        if (rFileLocation == null) {
            return saveWorkflowAs(workflowContext);
        } else {
            assert rFileLocation.getName().endsWith(FORMAT_SUFFIX);

            workflowContext.save();
            workflowContext.setChanged(false);

            return true;
        }
    }


    public void closeAll() {
        processUnsavedChanges();

        List<WorkflowContext> allContexts = new ArrayList<WorkflowContext>(getWorkflows());

        for (WorkflowContext sc : allContexts) {
            unRegisterSceneContext(sc);
        }
    }


    /**
     * collect all modified but still unchanged workflows and save them
     */
    public void processUnsavedChanges() {

        for (WorkflowContext workflowContext : workflowContexts) {
            if (workflowContext.wasChanged()) {
                String msg = "There are unsaved changes in the current workflow.\nDo you want to save them?";
                int status = JOptionPane.showConfirmDialog(null, msg, "Workflow modified", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

                if (status == JOptionPane.YES_OPTION) {
                    if (workflowContext.getLocation() != null)
                        workflowContext.save();
                    else
                        saveWorkflowAs(workflowContext);
                }
            }
        }
    }


    /**
     * Adds a new listener.
     */
    public void addSesManListener(SesManListener l) {
        if (l == null)
            return;

        sesManListeners.add(l);
    }


    /**
     * Removes a listener.
     */
    public void removeSesManListener(SesManListener l) {
        if (l == null)
            return;

        sesManListeners.remove(l);
    }

}